use std::str::FromStr;

use crate::{
    models::{
        message::{MessageModelDTO, MessageReceiverType},
        user::NormalUserModel,
    },
    services::{
        club_service::ClubService, message_service::MessageService, user_service::UserService,
    },
    AppResult, PaginationQuery,
};
use actix_web::{web, HttpResponse};
use wither::bson::oid::ObjectId;

use super::OK_RESPONSE;

mod ctrl_struct {
    use std::str::FromStr;

    use crate::{
        models::message::MessageReceiver,
        models::user::SimpleUserModel,
        services::{
            club_service::ClubService, message_service::CreateMessageReq, user_service::UserService,
        },
        utils::db_utils::json_to_bson,
        AppResult,
    };
    use serde::{Deserialize, Serialize};
    use serde_json::Value;
    use wither::bson::oid::ObjectId;
    #[derive(Debug, Clone, Serialize, Deserialize)]
    #[serde(tag = "type")]
    pub enum SendMessageReq {
        #[serde(rename = "club")]
        Club {
            title: String,
            content: String,
            club_id: String,
            #[serde(default = "Default::default")]
            extra: Value,
        },
        #[serde(rename = "pointer")]
        Pointer {
            receiver_id: String,
            title: String,
            content: String,
            #[serde(default = "Default::default")]
            extra: Value,
        },
        #[serde(rename = "comment")]
        Comment {
            title: String,
            content: String,
            club_id: String,
            #[serde(default = "Default::default")]
            extra: Value,
        },
        #[serde(rename = "reply")]
        Reply {
            receiver_id: String,
            parent_id: String,
            title: String,
            content: String,
            #[serde(default = "Default::default")]
            extra: Value,
        },
        #[serde(rename = "article")]
        Article {
            article_id: String,
            title: String,
            content: String,
            #[serde(default = "Default::default")]
            extra: Value,
        },
        #[serde(rename = "activity")]
        Activity {
            activity_id: String,
            title: String,
            content: String,
            #[serde(default = "Default::default")]
            extra: Value,
        },
    }

    impl SendMessageReq {
        pub async fn into_create_message_req<T: Into<SimpleUserModel>>(
            self,
            club_service: &ClubService,
            user_service: &UserService,
            sender: T,
        ) -> AppResult<CreateMessageReq> {
            match self {
                SendMessageReq::Club {
                    title,
                    content,
                    club_id,
                    extra,
                } => {
                    let oid = ObjectId::from_str(&club_id)?;
                    let club = club_service.club_detail(oid).await?;
                    Ok(CreateMessageReq {
                        sender: Some(sender.into()),
                        title,
                        content,
                        receiver: MessageReceiver::Club(club.into()),
                        extra: Some(json_to_bson(&extra)),
                    })
                }
                SendMessageReq::Pointer {
                    receiver_id,
                    title,
                    content,
                    extra,
                } => {
                    let receiver_id = ObjectId::from_str(&receiver_id)?;
                    let receiver = user_service.user_detail(receiver_id).await?;
                    Ok(CreateMessageReq {
                        sender: Some(sender.into()),
                        title,
                        content,
                        receiver: MessageReceiver::Pointer(receiver.into()),
                        extra: Some(json_to_bson(&extra)),
                    })
                }
                SendMessageReq::Comment {
                    title,
                    content,
                    club_id,
                    extra,
                } => {
                    let club_oid = ObjectId::from_str(&club_id)?;
                    let club = club_service.club_detail(club_oid).await?;
                    Ok(CreateMessageReq {
                        sender: Some(sender.into()),
                        title,
                        content,
                        receiver: MessageReceiver::Comment(club.into()),
                        extra: Some(json_to_bson(&extra)),
                    })
                }
                SendMessageReq::Reply {
                    receiver_id,
                    parent_id,
                    title,
                    content,
                    extra,
                } => {
                    let receiver_id = ObjectId::from_str(&receiver_id)?;
                    let parent_id = ObjectId::from_str(&parent_id)?;
                    user_service.user_detail(receiver_id).await?;
                    Ok(CreateMessageReq {
                        sender: Some(sender.into()),
                        receiver: MessageReceiver::Reply {
                            id: parent_id,
                            user_id: receiver_id,
                        },
                        title,
                        content,
                        extra: Some(json_to_bson(&extra)),
                    })
                }
                Self::Activity {
                    activity_id,
                    title,
                    content,
                    extra,
                } => {
                    let activity_id = ObjectId::from_str(&activity_id)?;
                    // TODO: id有效性检查
                    Ok(CreateMessageReq {
                        sender: Some(sender.into()),
                        receiver: MessageReceiver::Activity { id: activity_id },
                        title,
                        content,
                        extra: Some(json_to_bson(&extra)),
                    })
                }
                Self::Article {
                    article_id,
                    title,
                    content,
                    extra,
                } => {
                    let article_id = ObjectId::from_str(&article_id)?;
                    // TODO: id有效性检查
                    Ok(CreateMessageReq {
                        sender: Some(sender.into()),
                        receiver: MessageReceiver::Article { id: article_id },
                        title,
                        content,
                        extra: Some(json_to_bson(&extra)),
                    })
                }
            }
        }
    }
    #[derive(Debug, Clone, Serialize, Deserialize)]
    pub struct MarkMessageToReadReq {
        pub message_id: String,
    }
    impl MarkMessageToReadReq {
        pub fn get_message_id_as_object_id(&self) -> AppResult<ObjectId> {
            Ok(ObjectId::from_str(&self.message_id)?)
        }
    }
    define_filter_request_struct!(SearchMySendedMessageReq {});
    define_filter_request_struct!(SearchMyReceivedMessageReq {});
    define_filter_request_struct!(SearchClubMessageReq { club_id: String });
    define_filter_request_struct!(SearchClubCommentMessageReq { club_id: String });
    define_filter_request_struct!(SearchActivityMessage {
        activity_id: String
    });
    define_filter_request_struct!(SearchArticleMessage { article_id: String });
    define_request_struct!(MessageDetailReq { message_id: String });
}

async fn send_message(
    message_service: web::Data<MessageService>,
    club_service: web::Data<ClubService>,
    user_service: web::Data<UserService>,
    user: NormalUserModel,
    req: web::Json<ctrl_struct::SendMessageReq>,
) -> AppResult<HttpResponse> {
    let create_req = req
        .into_inner()
        .into_create_message_req(&club_service, &user_service, user)
        .await?;
    let res = message_service.create_message(create_req).await?;
    Ok(HttpResponse::Ok().json(MessageModelDTO::from(res)))
}

async fn search_my_sended_message(
    message_service: web::Data<MessageService>,
    user: NormalUserModel,
    query: web::Query<PaginationQuery>,
    req: web::Json<ctrl_struct::SearchMySendedMessageReq>,
) -> AppResult<HttpResponse> {
    let mut filter = req.get_filter_as_document_use_default().unwrap_or_default();
    filter.insert("sender.id", user.id);

    let res = message_service.find_all_message(filter, &query).await?;

    Ok(HttpResponse::Ok().json(res.map_into::<MessageModelDTO>()))
}

async fn search_my_received_message(
    message_service: web::Data<MessageService>,
    user: NormalUserModel,
    query: web::Query<PaginationQuery>,
    req: web::Json<ctrl_struct::SearchMyReceivedMessageReq>,
) -> AppResult<HttpResponse> {
    let filter = req.get_filter_as_document_use_default();
    let res = message_service
        .find_user_receiver_message(user.id, filter, &query.into_inner())
        .await?;
    Ok(HttpResponse::Ok().json(res.map_into::<MessageModelDTO>()))
}

async fn mark_message_to_read(
    message_service: web::Data<MessageService>,
    user: NormalUserModel,
    req: web::Json<ctrl_struct::MarkMessageToReadReq>,
) -> AppResult<HttpResponse> {
    let req = req.into_inner();
    message_service
        .mark_message_to_read(req.get_message_id_as_object_id()?, user)
        .await?;
    Ok(HttpResponse::Ok().json(OK_RESPONSE))
}

async fn message_detail(
    message_service: web::Data<MessageService>,
    req: web::Json<ctrl_struct::MessageDetailReq>,
) -> AppResult<HttpResponse> {
    let message_id = ObjectId::from_str(&req.message_id)?;
    let res = message_service.message_detail(message_id).await?;
    Ok(HttpResponse::Ok().json(MessageModelDTO::from(res)))
}

async fn search_club_message(
    message_service: web::Data<MessageService>,
    query: web::Query<PaginationQuery>,
    req: web::Json<ctrl_struct::SearchClubMessageReq>,
) -> AppResult<HttpResponse> {
    let mut filter = req.get_filter_as_document_use_default().unwrap_or_default();
    filter.insert("receiver.type", MessageReceiverType::Club);
    filter.insert("receiver.id", ObjectId::from_str(&req.club_id)?);

    let res = message_service.find_all_message(filter, &query).await?;
    Ok(HttpResponse::Ok().json(res.map_into::<MessageModelDTO>()))
}
async fn search_club_comment_message(
    message_service: web::Data<MessageService>,
    query: web::Query<PaginationQuery>,
    req: web::Json<ctrl_struct::SearchClubCommentMessageReq>,
) -> AppResult<HttpResponse> {
    let mut filter = req.get_filter_as_document_use_default().unwrap_or_default();
    filter.insert("receiver.id", ObjectId::from_str(&req.club_id)?);
    filter.insert("receiver.type", MessageReceiverType::Comment);

    let res = message_service.find_all_message(filter, &query).await?;
    Ok(HttpResponse::Ok().json(res.map_into::<MessageModelDTO>()))
}

async fn search_activity_message(
    message_service: web::Data<MessageService>,
    query: web::Query<PaginationQuery>,
    req: web::Json<ctrl_struct::SearchActivityMessage>,
) -> AppResult<HttpResponse> {
    let mut filter = req.get_filter_as_document_use_default().unwrap_or_default();
    filter.insert("receiver.id", ObjectId::from_str(&req.activity_id)?);
    filter.insert("receiver.type", MessageReceiverType::Activity);

    let res = message_service.find_all_message(filter, &query).await?;
    Ok(HttpResponse::Ok().json(res.map_into::<MessageModelDTO>()))
}

async fn search_article_message(
    message_service: web::Data<MessageService>,
    query: web::Query<PaginationQuery>,
    req: web::Json<ctrl_struct::SearchArticleMessage>,
) -> AppResult<HttpResponse> {
    let mut filter = req.get_filter_as_document_use_default().unwrap_or_default();
    filter.insert("receiver.id", ObjectId::from_str(&req.article_id)?);
    filter.insert("receive.type", MessageReceiverType::Article);

    let res = message_service.find_all_message(filter, &query).await?;
    Ok(HttpResponse::Ok().json(res.map_into::<MessageModelDTO>()))
}

pub fn configure_message_controller(config: &mut web::ServiceConfig) {
    config
        .service(
            web::scope("/send")
                .route("", web::post().to(send_message))
                .route("/search", web::post().to(search_my_sended_message)),
        )
        .service(
            web::scope("/received").route("/search", web::post().to(search_my_received_message)),
        )
        .service(web::resource("/mark").route(web::post().to(mark_message_to_read)))
        .service(web::resource("/detail").route(web::post().to(message_detail)))
        .service(
            web::scope("/search")
                .route("/club", web::post().to(search_club_message))
                .route("/club/comment", web::post().to(search_club_comment_message))
                .route("/activity", web::post().to(search_activity_message))
                .route("/article", web::post().to(search_article_message)),
        );
}
